from .assess import Analyzer
from .simulation import simulation
import xml.etree.ElementTree as ET
from .extract import extract
from .generate_node import generate_node_hierarchy
from .change_status import make_active_node
from .get_ancestor import get_ancestor
from .const_args import Dist
from .dist import Exponential,Weibull,Uniform,LogNormal,Normal,Binomial, Fixed,Dirac,SoF
from .pms import Task
from .node import Node
import os,json
import copy

class Solver():

    def __init__(self,xml_str,subject):
        self.filepath = xml_str
        self.subject = subject
    
    def run(self):
        pass

class Desimulator(Solver):
    
    def __init__(self,xml_str,subject,nums,slice):
        super().__init__(xml_str,subject)
        self.sim_nums = int(nums)
        self.slice = float(slice)

        self.root = ET.fromstring(xml_str)
        self.tasks = self.get_tasks(self.root)

        failure_models = self.get_failure_models(self.root)
        repair_models = self.get_repair_models(self.root)
        self.nodes=[]
        generate_node_hierarchy(self.nodes, self.root,failure_models,repair_models)

    def write_file(self,data):
        file_path = os.path.join('D:/',"status.json")
        with open(file_path, 'w', encoding='utf-8') as file:
            for key in data:
                json_str = json.dumps(data[key],ensure_ascii=False)
                file.write(json_str)

    def run(self,taskId):
        results={}
        results2={}
        counter = 1
        process_interval = self.sim_nums*1.0/100     

        self.basic_nodes0 = {}     # 记录全部根节点，任务中的 basic_node 表示本次任务用到节点
        basic_nodes = self.nodes[0].find_basic()  # 先找到根节点的所有基节点 
        for node in basic_nodes:
            if node.name not in self.basic_nodes0:
                new_node = Node(node.name)
                new_node.is_cyc = node.is_cyc
                new_node.failure_dist = node.failure_dist
                new_node.repair_dist = node.repair_dist
                new_node.cyc = node.cyc
                new_node.status = node.status
                new_node.failure_time = node.failure_time

                # new_node = copy.deepcopy(node)
                self.basic_nodes0[node.name] = new_node

        for i in range(self.sim_nums):
            #print(i)
            data,data_continous = simulation(self.tasks,self.nodes,self.basic_nodes0)
            for key in data:
                if i == 0:
                    results[key] = []
                    results2[key] = []
                results[key].append(data[key])
                results2[key].append(data_continous[key])
            if i==0:
                self.subject.notify_observers({taskId+'status': results}) # 返回第一次的单元状态变化过程
                self.write_file(results2)
            
            # 返回仿真的进度
            if self.sim_nums <100:
                self.subject.notify_observers({taskId+'calcprog': (int)((i+1)/process_interval)})
            else:
                if (i+1) >= (int)(counter * process_interval) and (i+1) <= (int)((counter+1)*process_interval):
                    if counter<=99:
                        self.subject.notify_observers({taskId+'calcprog': counter})
                        counter += 1

            print('仿真次数${}'.format(i))
        
        return results,results2
    
    def get_failure_models(self,root):
        failure_models={}
        for failure_model in root.find('failure_models').findall('failure_model'):
            fm_name = failure_model.attrib['name']
            fm_type = failure_model.attrib['type']
            argus = []
            if fm_name not in failure_model:                
                if fm_type==Dist.exp:
                    for child in failure_model:
                        argus.append((float)(child.attrib['value']))
                    dist = Exponential(Dist.exp,argus)
                elif fm_type == Dist.weibull:
                    for child in failure_model:
                        argus.append((float)(child.attrib['value']))
                    dist = Weibull(Dist.weibull,argus)
                elif fm_type == Dist.lognormal:
                    for child in failure_model:
                        argus.append((float)(child.attrib['value']))
                    dist = LogNormal(Dist.lognormal,argus)
                elif fm_type == Dist.normal:
                    for child in failure_model:
                        argus.append((float)(child.attrib['value']))
                    dist = Normal(Dist.normal,argus)
                elif fm_type == Dist.uniform:
                    for child in failure_model:
                        argus.append((float)(child.attrib['value']))
                    dist = Uniform(Dist.uniform,argus)
                elif fm_type == Dist.bernoulli:
                    for child in failure_model:
                        argus.append((float)(child.attrib['value']))
                    dist = Fixed(Dist.bernoulli,argus)
                elif fm_type == Dist.binomial:
                    for child in failure_model:
                        argus.append((float)(child.attrib['value']))
                    dist = Binomial(Dist.binomial,argus)
                elif fm_type == Dist.sof:
                    for child in failure_model:
                        argus.append((float)(child.attrib['value']))
                    dist = SoF(Dist.sof,argus)
                failure_models[fm_name] = dist

        return failure_models
    def get_repair_models(self,root):
        repair_models={}
        if root.find('repair_models') is not None:
            for repair_model in root.find('repair_models').findall('repair_model'):
                rm_name = repair_model.attrib['name']
                rm_type = repair_model.attrib['type']
                argus = []
                if rm_name not in repair_models: 
                    if rm_type==Dist.exp:
                        for child in repair_model:
                            argus.append((float)(child.attrib['value']))
                        dist = Exponential(Dist.exp,argus)
                    elif rm_type == Dist.weibull:
                        for child in repair_model:
                            argus.append((float)(child.attrib['value']))
                        dist = Weibull(Dist.weibull,argus)
                    elif rm_type == Dist.lognormal:
                        for child in repair_model:
                            argus.append((float)(child.attrib['value']))
                        dist = LogNormal(Dist.lognormal,argus)
                    elif rm_type == Dist.normal:
                        for child in repair_model:
                            argus.append((float)(child.attrib['value']))
                        dist = Normal(Dist.normal,argus)
                    elif rm_type == Dist.uniform:
                        for child in repair_model:
                            argus.append((float)(child.attrib['value']))
                        dist = Uniform(Dist.uniform,argus)
                    elif rm_type == Dist.bernoulli:
                        for child in repair_model:
                            argus.append((float)(child.attrib['value']))
                        dist = Fixed(Dist.bernoulli,argus)
                    elif rm_type == Dist.dirac:
                        for child in repair_model:
                            argus.append((float)(child.attrib['value']))
                        dist = Dirac(Dist.dirac,argus)

                    repair_models[rm_name] = dist
        return repair_models
    
    def get_tasks(self,root):
        tasks = []
        id = 0
        for task in root.find('tasks').findall('task'):
            task_duration = float(task.attrib['duration'])
            model_name = task.attrib['model']
            tk = Task(id,'',task_duration,model_name)
            if 'NAM' in task.attrib:
                tk.nam_names = task.attrib['NAM'].split(",")
            tasks.append(tk)
            id+=1
        return tasks


           